package com.zeus.gateway.oauth2Config;

import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @Author fangyi
 * @Date 2023/7/15 23:37
 * @Version 1.0
 */
public class ReactiveJdbcAuthManager implements ReactiveAuthenticationManager {

    @Resource
    private TokenStore tokenStore;


    public ReactiveJdbcAuthManager(DataSource dataSource) {
        this.tokenStore = new JdbcTokenStore(dataSource);
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.justOrEmpty(authentication)
                .filter(a->a instanceof BearerTokenAuthenticationToken)
                .cast(BearerTokenAuthenticationToken.class)
                .map(BearerTokenAuthenticationToken :: getToken)
                .flatMap(accessToken ->{
                    OAuth2AccessToken oAuth2AccessToken = this.tokenStore.readAccessToken(accessToken);
                    if(oAuth2AccessToken == null){
                        return Mono.error(new InvalidTokenException("Token not found in token store!"));
                    }
                    if(oAuth2AccessToken.isExpired()){
                        return Mono.error(new InvalidTokenException("Token is expired"));
                    }

                    OAuth2Authentication oAuth2Authentication = this.tokenStore.readAuthentication(accessToken);
                    if(oAuth2Authentication == null){
                        return Mono.error(new InvalidTokenException("Invalid token!"));
                    }
                    if(!oAuth2Authentication.isAuthenticated()){
                        return Mono.error(new InvalidTokenException("Token not Authenticated"));
                    }
                    return Mono.justOrEmpty(oAuth2Authentication);
                }).cast(Authentication.class);
    }
}
